package server_msg // Package msg 定义包名为msg

import (
	"errors" // 导入错误处理包，用于创建和处理错误
	"fmt"
	"google.golang.org/protobuf/reflect/protoreflect" // 导入protobuf反射包，用于处理protobuf消息的元数据
	"log"
	"sen-golang-study/go_common"
	"sen-golang-study/go_game_study/hero_story_go_server/common/server_msg/proto_msg"
	"sync"
)

// 定义全局变量：消息代码到消息描述符的映射表
// key是int16类型的消息代码，value是protobuf消息描述符
var msgCodeAndMsgDescMap = make(map[int16]protoreflect.MessageDescriptor)

// 定义全局变量：消息名称到消息代码的映射表
// key是string类型的消息名称，value是int16类型的消息代码
var msgNameAndMsgCodeMap = make(map[string]int16)

var initMapLocker = sync.Mutex{}

// GetMsgDescByMsgCode 根据消息代码获取消息描述符的函数
// 参数：msgCode - 消息代码（int16类型）
// 返回值：消息描述符和可能的错误
func GetMsgDescByMsgCode(msgCode int16) (protoreflect.MessageDescriptor, error) {
	if msgCode < 0 { // 检查消息代码是否有效（不能为负数）
		return nil, errors.New("消息编号无效") // 如果无效，返回nil和错误信息
	}

	if len(msgNameAndMsgCodeMap) <= 0 || len(msgCodeAndMsgDescMap) <= 0 {
		err := initMap()
		if err != nil {
			return nil, err
		}
	}
	return msgCodeAndMsgDescMap[msgCode], nil // 从映射表中获取对应的消息描述符并返回
}

// GetMsgCodeByMsgName 根据消息名称获取消息代码的函数
// 参数：msgName - 消息名称（string类型）
// 返回值：消息代码和可能的错误
func GetMsgCodeByMsgName(msgName string) (int16, error) {
	if len(msgName) <= 0 { // 检查消息名称是否为空
		return -1, errors.New("消息名词为空") // 如果为空，返回-1和错误信息
	}

	return msgNameAndMsgCodeMap[msgName], nil // 从映射表中获取对应的消息代码并返回
}

func initMap() error {
	initMapLocker.Lock()
	defer initMapLocker.Unlock()

	err := initMsgNameAndDescMap()
	if err != nil {
		return err
	}
	err = initMsgCodeAndDescMap()
	if err != nil {
		return err
	}
	return nil
}

// 初始化消息名称和描述字典数据
func initMsgNameAndDescMap() error {
	if len(msgNameAndMsgCodeMap) > 0 {
		println("消息名称和描述字典数据已初始化。")
		return nil
	}

	// 将游戏消息协议中的消息字段转换为驼峰
	for k, v := range proto_msg.MsgCode_value {
		// 将转换为驼峰后的消息字段作为消息的名称
		msgName := go_common.ToPascalCase(k)
		msgNameAndMsgCodeMap[msgName] = int16(v)
	}

	return nil
}

// 初始化消息名和编号的字典数据
func initMsgCodeAndDescMap() error {
	if len(msgCodeAndMsgDescMap) > 0 {
		println("消息编码与描述字典已初始化。")
		return nil
	}

	msgDescList := proto_msg.File_game_msg_protocol_proto.Messages()
	for i := 0; i < msgDescList.Len(); i++ {
		msgDesc := msgDescList.Get(i)
		msgName := go_common.ToPascalCase(string(msgDesc.Name()))

		msgCode, err := GetMsgCodeByMsgName(msgName)
		if err != nil {
			// 记录错误日志
			log.Printf("Failed to get message code for '%s': %v", msgName, err)
			// 包装并返回错误
			return fmt.Errorf("message processing failed: %w", err)
		}
		msgCodeAndMsgDescMap[msgCode] = msgDesc
	}

	return nil
}
